home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / lang / c-part2 / 15457 < prev    next >
Encoding:
Text File  |  1996-08-05  |  14.0 KB  |  626 lines

  1. Path: news.ucdavis.edu!quad!knight
  2. From: knight@quad.cs.ucdavis.edu (James Knight)
  3. Newsgroups: comp.lang.c,comp.unix.programmer
  4. Subject: Re: Q: '\n' character  -  Making a better fgets?
  5. Followup-To: comp.lang.c,comp.unix.programmer
  6. Date: 18 Apr 1996 22:28:10 GMT
  7. Organization: University of California, Davis
  8. Message-ID: <4l6flq$rck@mark.ucdavis.edu>
  9. References: <31616F63.481D@lava.weeg.uiowa.edu> <4jtddt$eu7@masala.cc.uh.edu> <DpBuF6.83C@ukpsshp1.serigate.philips.nl> <3169994D.665ACF69@cs.ucl.ac.uk>
  10. NNTP-Posting-Host: quad.cs.ucdavis.edu
  11. X-Newsreader: TIN [version 1.2 PL2]
  12.  
  13.  
  14. The experiment I mentioned on varying fgets/fputs and fgetc/fputc was
  15. run using a simple implementation of the reverse program (i.e.,
  16. reverse the characters of each line).  I don't have the original
  17. experiment results except on paper, but I've recreated the experiment.
  18.  
  19. For each variation of the program, I ran it using two inputs, a 4.5MB
  20. file containing all short lines (i.e., 80 chars or less) and a 1MB
  21. file consisting of a single line.  The variations were:
  22.  
  23.    1) using fgetc/fputc to do I/O
  24.    2) using getc/putc to do I/O
  25.    3) using fgets/fputs to do I/O  (limited to lines of 256 chars or less)
  26.    4) using my_getline/fputs to do I/O
  27.    5) using freadln/fputs to do I/O
  28.    6) same as 4, except reimplementing my_getline to use getc
  29.    7) an optimized version of 6
  30.    8) using read/write to read and write the complete file
  31.  
  32. The code for these examples is given below.
  33.  
  34. I compiled each of them using "gcc -O2 -p" using gcc 2.6.3 on a
  35. Decstation 3100/240 running Ultrix 4.3A.  I then ran each of them four
  36. times on each input, taking the "prof" time as the total running
  37. time (except that the "prof" time does not reflect the actual running
  38. time, because the Decstations don't adjust for the clock speed.  But
  39. the relative differences in the scores should reflect the actual
  40. running time).  The time in the table is the average of the middle two
  41. runs (I drop the top and bottom).
  42.  
  43.                      4.5MB,              1MB,
  44.                    short lines         one line
  45.                    -----------         --------
  46. 1) fgetc/fputc       23.00               5.32
  47. 2) getc/putc         17.56               4.09
  48. 3) fgets/fputs       17.80               N/A
  49. 4) my_getline        17.52               3.96
  50. 5) freadln           19.70               4.20
  51. 6) reimplement       19.82               4.44
  52. 7) optimize          18.22               4.22
  53. 8) read/write         9.65               1.46
  54.  
  55.  
  56. Now, this actually suprised the heck out of me, so I reran the test
  57. for 2, 3, 4 and 5 a number of times.  Even though I wrote my_getline,
  58. I never expected it to be as fast as the getc/putc version.  Could
  59. someone try to verify these results?  Or look at the source for the
  60. programs and try to optimize 2 so that it's better than 4 (I've tried
  61. once without success).  This can't really be right, unless the Ultrix
  62. creators have optimized fgets and strlen enough to offset my
  63. implementation.
  64.  
  65. Also, you may think that the code I wrote to do the actual reversal
  66. isn't optimized.  You would be right.  You would also be missing the
  67. point, which is to vary only the I/O code.  The reversal code gives
  68. the program a computational piece which does a non-trivial computation
  69. but which doesn't overwhelm the I/O effect as far as the profiled
  70. timing is concerned.
  71.  
  72. Jim
  73.  
  74.  
  75. #-------------------------------
  76. #
  77. # Code for version 1
  78. #
  79. #-------------------------------
  80.  
  81. #include <stdio.h>
  82. #include <stdlib.h>
  83.  
  84. int main()
  85. {
  86.   int i, j, len, linelen;
  87.   char ch, tch, *line;
  88.  
  89.   line = malloc(128);
  90.   linelen = 128;
  91.  
  92.   len = 0;
  93.   while ((ch = fgetc(stdin)) != EOF) {
  94.     if (ch != '\n') {
  95.       if (len + 1 == linelen) {
  96.         linelen += linelen;
  97.         line = realloc(line, linelen);
  98.       }
  99.  
  100.       line[len] = ch;
  101.       len++;
  102.     }
  103.     else {
  104.       if (len > 0) {
  105.         for (i=0,j=len-1; i < j; i++,j--) {
  106.           tch = line[i];
  107.           line[i] = line[j];
  108.           line[j] = tch;
  109.         }
  110.  
  111.         for (i=0; i < len; i++)
  112.           fputc(line[i], stdout);
  113.       }
  114.       fputc('\n', stdout);
  115.       len = 0;
  116.     }
  117.   }
  118.  
  119.   if (len > 0) {
  120.     for (i=0,j=len-1; i < j; i++,j--) {
  121.       tch = line[i];
  122.       line[i] = line[j];
  123.       line[j] = tch;
  124.     }
  125.  
  126.     for (i=0; i < len; i++)
  127.       fputc(line[i], stdout);
  128.   }
  129.  
  130.   return 0;
  131. }
  132.  
  133.  
  134.  
  135. #-------------------------------
  136. #
  137. # Code for version 2
  138. #
  139. #-------------------------------
  140.  
  141.  
  142. #include <stdio.h>
  143. #include <stdlib.h>
  144.  
  145. int main()
  146. {
  147.   int i, j, len, linelen;
  148.   char ch, tch, *line;
  149.  
  150.   line = malloc(128);
  151.   linelen = 128;
  152.  
  153.   len = 0;
  154.   while ((ch = getc(stdin)) != EOF) {
  155.     if (ch != '\n') {
  156.       if (len + 1 == linelen) {
  157.         linelen += linelen;
  158.         line = realloc(line, linelen);
  159.       }
  160.  
  161.       line[len] = ch;
  162.       len++;
  163.     }
  164.     else {
  165.       if (len > 0) {
  166.         for (i=0,j=len-1; i < j; i++,j--) {
  167.           tch = line[i];
  168.           line[i] = line[j];
  169.           line[j] = tch;
  170.         }
  171.  
  172.         for (i=0; i < len; i++)
  173.           putc(line[i], stdout);
  174.       }
  175.       putc('\n', stdout);
  176.       len = 0;
  177.     }
  178.   }
  179.  
  180.   if (len > 0) {
  181.     for (i=0,j=len-1; i < j; i++,j--) {
  182.       tch = line[i];
  183.       line[i] = line[j];
  184.       line[j] = tch;
  185.     }
  186.  
  187.     for (i=0; i < len; i++)
  188.       putc(line[i], stdout);
  189.   }
  190.  
  191.   return 0;
  192. }
  193.  
  194.  
  195.  
  196. #-------------------------------
  197. #
  198. # Code for version 3
  199. #
  200. #-------------------------------
  201.  
  202.  
  203. #include <stdio.h>
  204. #include <stdlib.h>
  205.  
  206. int main()
  207. {
  208.   int i, j, len, linelen;
  209.   char ch, tch, *line;
  210.  
  211.   line = malloc(256);
  212.   linelen = 256;
  213.  
  214.   while (fgets(line, linelen, stdin) != NULL) {
  215.     len = strlen(line);
  216.     if (len > 0) {
  217.       for (i=0,j=len-1; i < j; i++,j--) {
  218.         tch = line[i];
  219.         line[i] = line[j];
  220.         line[j] = tch;
  221.       }
  222.  
  223.       fputs(line, stdout);
  224.     }
  225.     putc('\n', stdout);
  226.   }
  227.  
  228.   return 0;
  229. }
  230.  
  231.  
  232.  
  233. #-------------------------------
  234. #
  235. # Code for version 4
  236. #
  237. #-------------------------------
  238.  
  239.  
  240. #include <stdio.h>
  241. #include <stdlib.h>
  242.  
  243. char *my_getline(FILE *fp, int *len_out);
  244.  
  245. int main()
  246. {
  247.   int i, j, len, linelen;
  248.   char ch, tch, *line;
  249.  
  250.   while ((line = my_getline(stdin, &len)) != NULL) {
  251.     if (len > 0) {
  252.       for (i=0,j=len-1; i < j; i++,j--) {
  253.         tch = line[i];
  254.         line[i] = line[j];
  255.         line[j] = tch;
  256.       }
  257.  
  258.       fputs(line, stdout);
  259.     }
  260.     putc('\n', stdout);
  261.   }
  262.  
  263.   return 0;
  264. }
  265.  
  266.  
  267. /*
  268.  * my_getline
  269.  *
  270.  * Read a line of any length, store it in an internal buffer, and 
  271.  * return the internal buffer (along with a length value if desired).
  272.  *
  273.  * NOTE: Each line read will overwrite the previous line read.  So,
  274.  *       make a copy of any line you want to keep around.
  275.  *
  276.  * Parameters:
  277.  *     fp - A FILE pointer open for reading.
  278.  *     len_out - Address to where to store the line length.
  279.  *
  280.  * Returns:
  281.  *     An internal buffer containing the line, or NULL on EOF or error.
  282.  */
  283. char *my_getline(FILE *fp, int *len_out)
  284. {
  285.   static int bufsize = 0;
  286.   static char *buffer = NULL;
  287.   int size, len, flag;
  288.  
  289.   /*
  290.    * Initialize the internal buffer, if necessary.
  291.    */
  292.   if (buffer == NULL) {
  293.     bufsize = 128;
  294.     if ((buffer = malloc(bufsize)) == NULL)
  295.       return NULL;
  296.   }  
  297.  
  298.   /*
  299.    * Read the first part of the line.
  300.    */
  301.   flag = 0;
  302.   buffer[bufsize-2] = '\0';
  303.  
  304.   if (fgets(buffer, bufsize, fp) == NULL)
  305.     return NULL;
  306.   else if (buffer[bufsize-2] == '\0' || buffer[bufsize-2] == '\n') {
  307.     len = strlen(buffer);
  308.     flag = 1;
  309.   }
  310.  
  311.   /*
  312.    * If the line is longer, then realloc the internal buffer and
  313.    * read the next section of the line.
  314.    */
  315.   while (!flag) {
  316.     size = bufsize - 1;
  317.     bufsize += bufsize;
  318.     if ((buffer = realloc(buffer, bufsize)) == NULL)
  319.       return NULL;
  320.  
  321.     buffer[bufsize-2] = '\0';
  322.     if (fgets(buffer + size, bufsize - size, fp) == NULL) {
  323.       len = size;
  324.       flag = 1;
  325.     }
  326.     else if (!buffer[bufsize-2] || buffer[bufsize-2] == '\n') {
  327.       len = size + strlen(buffer + size);
  328.       flag = 1;
  329.     }
  330.   }
  331.  
  332.   /*
  333.    * Strip the newline from the line, if it's there.
  334.    */
  335.   if (buffer[len-1] == '\n')
  336.     buffer[--len] = '\0';
  337.  
  338.   if (len_out) *len_out = len;
  339.   return buffer;
  340. }
  341.  
  342.  
  343.  
  344. #-------------------------------
  345. #
  346. # Code for version 5
  347. #
  348. #-------------------------------
  349.  
  350.  
  351. #include <stdio.h>
  352. #include <stdlib.h>
  353.  
  354. typedef enum fr_inflg {
  355.         FR_NOI =        0x00,           /* no input flags               */
  356.         FR_CONV =       0x01,           /* convert null chars to 127    */
  357.         FR_NL =         0x02,           /* treat nulls as newlines      */
  358.         FR_KEEP =       0x04            /* if NOMEM, keep partial line  */
  359. } fr_inflg;
  360.  
  361. typedef enum fr_outflg {
  362.         FR_NOO =        0x00,           /* no output flags              */
  363.         FR_EOF =        0x01,           /* EOF was encountered          */
  364.         FR_NONL =       0x02,           /* line did not end in newline  */
  365.         FR_NOMEM =      0x04,           /* out of memory                */
  366.         FR_OFLOW =      0x08            /* allocation size_t overflow   */
  367. } fr_outflg;
  368.  
  369. char *freadln(const fr_inflg inf, fr_outflg *ouf, size_t *len, FILE *stream);
  370.  
  371. #define CHAR_MAX 127
  372.  
  373. int main()
  374. {
  375.   int i, j, len, linelen;
  376.   char ch, tch, *line;
  377.   fr_outflg outflag;
  378.  
  379.   while ((line = freadln(FR_NOI, &outflag, &len, stdin)) != NULL &&
  380.          outflag != FR_EOF) {
  381.     if (len > 0) {
  382.       for (i=0,j=len-1; i < j; i++,j--) {
  383.         tch = line[i];
  384.         line[i] = line[j];
  385.         line[j] = tch;
  386.       }
  387.  
  388.       fputs(line, stdout);
  389.     }
  390.     putc('\n', stdout);
  391.  
  392.     free(line);
  393.   }
  394.  
  395.   return 0;
  396. }
  397.  
  398.  
  399. #define INIT_SIZE1      55              /* Fibonacci numbers            */
  400. #define INIT_SIZE2      89
  401.  
  402. char *freadln(const fr_inflg inf, fr_outflg *ouf, size_t *len, FILE *stream)
  403. {
  404.   size_t oldsize = INIT_SIZE1, cursize = INIT_SIZE2, newsize;
  405.   char *line, *pline, *limit, *new;
  406.  
  407.   *ouf = FR_NOO;
  408.  
  409.   pline = line = malloc(cursize);
  410.  
  411.   if (!line) {
  412.     *ouf = FR_NOMEM;
  413.     goto failure;
  414.   }
  415.  
  416.   limit = line + cursize - 2;     /* guarantee room for null      */
  417.  
  418.   while (1) {
  419.     int c = getc(stream);
  420.  
  421.     switch(c) {
  422.     case '\0':
  423.       if (inf & FR_NL)        /* nulls terminate      */
  424.         goto badend;
  425.       if (inf & FR_CONV) {    /* nulls get replaced   */
  426.         c = CHAR_MAX;
  427.         goto addchar;
  428.       }
  429.       goto addchar;
  430.       break;
  431.     case EOF:
  432.       *ouf = FR_EOF;
  433.       if (pline == line)      /* first character?     */
  434.         goto success;
  435.     badend:                         /* line end w/o newline */
  436.       *ouf |= FR_NONL;
  437.     case '\n':
  438.       goto success;           /* jump out of loop     */
  439.     addchar:
  440.     default:
  441.       *pline++ = c;
  442.       break;
  443.     }
  444.  
  445.     if (pline >= limit) {           /* if buffer is full... */
  446.       newsize = cursize + oldsize;
  447.       if (newsize < cursize) {        /* overflow!    */
  448.         *ouf = FR_OFLOW;
  449.         if (inf & FR_KEEP)
  450.           goto success;
  451.         free(line);
  452.         goto failure;
  453.       }
  454.       new = realloc(line, newsize);
  455.       if (!new) {
  456.         *ouf = FR_NOMEM;
  457.         if (inf & FR_KEEP)
  458.           goto success;
  459.         free(line);
  460.         goto failure;
  461.       }
  462.       oldsize = cursize;
  463.       cursize = newsize;
  464.       pline = new + (pline - line);
  465.       line = new;
  466.       limit = line + cursize - 2;
  467.     }
  468.   }
  469.  
  470. success:
  471.   *pline++ = '\0';                /* null-terminate               */
  472.   *len = pline - line - 1;        /* calculate line length        */
  473.   new = realloc(line, *len + 1);  /* try to trim buffer down      */
  474.   return (new) ? new : line;      /* if cannot, ah well...        */
  475.  
  476. failure:
  477.   return NULL;
  478. }
  479.  
  480.  
  481.  
  482. #-------------------------------
  483. #
  484. # Code for version 7
  485. #
  486. #-------------------------------
  487.  
  488. #include <stdio.h>
  489. #include <stdlib.h>
  490.  
  491. char *my_getline(FILE *fp, int *len_out);
  492.  
  493. int main()
  494. {
  495.   int i, j, len, linelen;
  496.   char ch, tch, *line;
  497.  
  498.   while ((line = my_getline(stdin, &len)) != NULL) {
  499.     if (len > 0) {
  500.       for (i=0,j=len-1; i < j; i++,j--) {
  501.         tch = line[i];
  502.         line[i] = line[j];
  503.         line[j] = tch;
  504.       }
  505.  
  506.       fputs(line, stdout);
  507.     }
  508.     putc('\n', stdout);
  509.   }
  510.  
  511.   return 0;
  512. }
  513.  
  514.  
  515. /*
  516.  * my_getline
  517.  *
  518.  * Read a line of any length, store it in an internal buffer, and 
  519.  * return the internal buffer (along with a length value if desired).
  520.  *
  521.  * NOTE: Each line read will overwrite the previous line read.  So,
  522.  *       make a copy of any line you want to keep around.
  523.  *
  524.  * Parameters:
  525.  *     fp - A FILE pointer open for reading.
  526.  *     len_out - Address to where to store the line length.
  527.  *
  528.  * Returns:
  529.  *     An internal buffer containing the line, or NULL on EOF or error.
  530.  */
  531. char *my_getline(FILE *fp, int *len_out)
  532. {
  533.   static int bufsize = 0;
  534.   static char *buffer = NULL;
  535.   int len;
  536.   char ch, *s;
  537.  
  538.   /*
  539.    * Initialize the internal buffer, if necessary.
  540.    */
  541.   if (buffer == NULL) {
  542.     bufsize = 128;
  543.     if ((buffer = malloc(bufsize)) == NULL)
  544.       return NULL;
  545.   }  
  546.  
  547.   /*
  548.    * The main loop reading characters.
  549.    */
  550.   len = 0;
  551.   s = buffer;
  552.   while ((ch = *s++ = getc(stdin)) != '\n' && ch != EOF) {
  553.     if (++len == bufsize) {
  554.       bufsize += bufsize;
  555.       if ((buffer = realloc(buffer, bufsize)) == NULL)
  556.         return NULL;
  557.       s = buffer + len;
  558.     }
  559.   }
  560.  
  561.   if (ch == EOF && len == 0)
  562.     return NULL;
  563.   else {
  564.     s[-1] = '\0';
  565.     if (len_out) *len_out = len;
  566.     return buffer;
  567.   }
  568. }
  569.  
  570.  
  571.  
  572. #-------------------------------
  573. #
  574. # Code for version 8
  575. #
  576. #-------------------------------
  577.  
  578. #include <stdio.h>
  579. #include <fcntl.h>
  580. #include <stdlib.h>
  581. #include <unistd.h>
  582. #include <sys/stat.h>
  583.  
  584. int main(int argc, char *argv[])
  585. {
  586.   int fd, size;
  587.   char *s, *t, *w, *buffer, tch;
  588.   struct stat sbuf;
  589.  
  590.   if (argc != 3) {
  591.     fprintf(stderr, "Usage: prog infile outfile\n");
  592.     exit(1);
  593.   }
  594.  
  595.   if (stat(argv[1], &sbuf) == -1 ||
  596.       (size = sbuf.st_size) == 0 ||
  597.       (buffer = malloc(size + 1)) == NULL ||
  598.       (fd = open(argv[1], O_RDONLY, 0666)) < 0 ||
  599.       read(fd, buffer, size) != size) {
  600.     fprintf(stderr, "Error during reading of input file.\n");
  601.     exit(1);
  602.   }
  603.   close(fd);
  604.  
  605.   for (s=buffer; *s; s++) {
  606.     for (t=s; *s && *s != '\n'; s++) ;
  607.  
  608.     if (s - t < 2)
  609.       continue;
  610.  
  611.     for (w=s-1; t < w; t++,w--) {
  612.       tch = *t;
  613.       *t = *w;
  614.       *w = tch;
  615.     }
  616.   }
  617.  
  618.   if ((fd = open(argv[2], O_WRONLY | O_CREAT, 0666)) < 0 ||
  619.       write(fd, buffer, size) < 0) {
  620.     fprintf(stderr, "Error during writing of output file.\n");
  621.     exit(1);
  622.   }
  623.  
  624.   return 0;
  625. }
  626.